using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Diagnostics;
using System.Web.Services.Protocols;
using Microsoft.SharePoint;

namespace Microsoft.EPM.TSAutoStatus
{
    public class UpdateStatus
    {
        #region Private Data
        private static ResourceDerived resource;
        private static StatusingDerived statusing;
        private static TimesheetDerived timesheet;
        private static string psurl = "";
        private static bool logEvents = false;
        private static bool logFileEveryHour = true;
        private static string logFolder = ".";
        private static string logPrefix = "TSAutoStatus";
        private static AppLog appLog = null;
        private static string hostName = "";
        private static EventEntry eventLog;
        #endregion

        #region Constructors
        private UpdateStatus()
        {
        }

        public UpdateStatus(string psUrl)
        {
            eventLog = new EventEntry();

            resource = new ResourceDerived();
            statusing = new StatusingDerived();
            timesheet = new TimesheetDerived();

            resource.Credentials = CredentialCache.DefaultCredentials;
            statusing.Credentials = CredentialCache.DefaultCredentials;
            timesheet.Credentials = CredentialCache.DefaultCredentials;

            if (psUrl.Length == 0)
            {
                eventLog.WriteEvent("Project Server URL not set.");
                return;
            }
            if (!psUrl.EndsWith("/")) psUrl = psUrl + "/";
            psurl = psUrl;
        }
        #endregion

        #region Public Properties
        public bool LoggingEnabled
        {
            get { return logEvents;}
            set { logEvents = value; }
        }

        public bool LogFileEveryHour
        {
            get { return logFileEveryHour;}
            set { logFileEveryHour = value; }
        }

        public string LoggingPrefix
        {
            get { return logPrefix; }
            set { logPrefix = value; }
        }

        public string LoggingFolder
        {
            get { return logFolder; }
            set { logFolder = value; }
        }

        public string HostName
        {
            get { return hostName; }
            set { hostName = value; }
        }
        #endregion

        #region Public Methods
        public bool ProcessTimesheet(Guid siteGuid, Guid tsGuid, string lcid, string userName, bool submitStatus, string sspName)
        {
            #region Local Variables
            string exceptionMsg = null;
            string tsInfo = string.Empty;
            Stopwatch timeToProcessAll = new Stopwatch();
            Stopwatch timeToProcessStatus = new Stopwatch();
            timeToProcessAll.Start();
            SPSite spSite = new SPSite(siteGuid);
            bool result = false;
            #endregion

            try
            {
                if (appLog == null) appLog = new AppLog(LoggingPrefix, LoggingFolder, LogFileEveryHour);

                #region STEP 1 - Initialize Web Service urls

                statusing.Url = WSUrl(spSite, HostName, sspName, "statusing", true);
                resource.Url = WSUrl(spSite, HostName, sspName, "resource", false);
                timesheet.Url = WSUrl(spSite, HostName, sspName, "timesheet", false);

                if (logEvents) 
                    appLog.WriteLine(string.Format("STEP 1 - Initialize Web Service urls"));

                #endregion

                #region STEP 2 - Retrieve Timesheet DataSet

                TimesheetWS.TimesheetDataSet timesheetDS = timesheet.ReadTimesheet(tsGuid);
                TimesheetPSI tsHeader = new TimesheetPSI(timesheetDS);
                Guid tspUID = tsHeader.PeriodUID;
                Guid tresUID = tsHeader.ResUID;

                if (logEvents)
                {
                    appLog.WriteLine(string.Format("STEP 2 - Start event override for timesheet UID: {0}", tsGuid.ToString()));
                    tsInfo = string.Format("Timesheet: {0}; Resource: {1}; Creator: {2}, Period: {3}",
                            tsHeader.Name, tsHeader.ResName, tsHeader.CreatorResName, tspUID.ToString());
                }
                #endregion

                #region STEP 3 - Perform statusing impersonation taking surrogate into account

                // Need to retrieve resource information to do proper impersonation
                ResourceWS.ResourceDataSet resourceDS = resource.ReadResource(tresUID);
                ResourcePSI resourcePSI = new ResourcePSI(resourceDS);

                // Check if surrogate timesheet
                if( (tsHeader.CreatorResUID != tsHeader.ResUID) // ower is different than creator
                    ||
                    (! resourcePSI.WindowsAccount.Equals(userName)) // case where timesheet was modified by TS manager
                    )
                {
                   statusing.SetImpersonationContext(resourcePSI.IsWindowsUser, resourcePSI.WindowsAccount, tresUID, Guid.Empty, siteGuid, lcid);
                }
                else
                {
                    // Faster check that calling the resource web service
                    bool isWindowsUser = userName.StartsWith("AspNetSqlMembershipProvider") ? false : true;
                    statusing.SetImpersonationContext(isWindowsUser, userName, tresUID, Guid.Empty, siteGuid, lcid);
                }

                if (logEvents) appLog.WriteLine(string.Format("STEP 3 - Statusing Impersonation for RES_UID: {0}", tresUID.ToString()));

                #endregion

                #region STEP 4 - Import Timesheet, will get an error if TS period is not within the Task's Start & Finish

                if (tspUID != Guid.Empty)
                    statusing.ImportTimesheet(tspUID);

                if (logEvents)
                    appLog.WriteLine(string.Format("STEP 4 - Imported Timesheet: {0}; Resource: {1}; Creator: {2}, TotalActValue: {3}, Period: {4}",
                    tsHeader.Name, tsHeader.ResName,
                    tsHeader.CreatorResName, tsHeader.TotalActValue.ToString("N2"), tspUID.ToString()));


                #endregion

                #region STEP 5 - Submit status update to project manager
                if (submitStatus)
                {
                    timeToProcessStatus.Start();
                    List<Guid> assignmentsUID = StatusingPSI.SubmitStatus(timesheetDS, appLog, logEvents);
                    if (assignmentsUID.Count > 0)
                    {
                        statusing.SubmitStatus(assignmentsUID.ToArray(), "Submitted to statusing");
                    }
                    if (logEvents)
                        appLog.WriteLine(string.Format("STEP 5 - SubmitStatus for: {0} assignments", assignmentsUID.Count.ToString()));

                    timeToProcessStatus.Stop();
                }
                result = true;
                #endregion

                #region STEP 6 - Handle Exceptions and logging
            }
            catch (SoapException ex)
            {
                exceptionMsg = GeneralHelper.HandleSoapException(ex);
                eventLog.WriteEvent("FAILED due to a SoapException: " + exceptionMsg);
            }
            catch (Exception ex)
            {
                exceptionMsg = ex.Message;
                eventLog.WriteEvent("FAILED due to an Exception: " + exceptionMsg);
            }
            finally
            {
                if (logEvents)
                {
                    if (exceptionMsg != null)
                    {
                        appLog.WriteLine(string.Format("FAILED due to an exception: {0}", exceptionMsg));
                        appLog.WriteLine(string.Format("SubmitStatus: {0}; timesheet URL: {1}; statusing URL: {2}",
                            submitStatus.ToString(), timesheet.Url, statusing.Url));
                        appLog.WriteLine(tsInfo);
                    }
                    else
                        appLog.WriteLine("SUCCESSFULY executed custom timesheet event OnUpdated");

                    appLog.WriteLine(string.Format("Total execution time: {0} seconds; SubmitStatus: {1} seconds\r\n",
                        timeToProcessAll.Elapsed.TotalSeconds.ToString("N"),
                        timeToProcessStatus.Elapsed.TotalSeconds.ToString("N")));
                }
            }
            return result;
            #endregion
        }

        public void FlushLog()
        {
            if (appLog != null)
            {
                appLog.Close();
                appLog = null;
            }
        }
        #endregion

        #region Private Methods
        private string WSUrl(SPSite ss, string hostName, string sspName, string wsName, bool impersonate)
        {
            string ssHostName = ss.HostName;
            if (hostName.Length > 0) ssHostName = hostName;
            if (impersonate)
                return string.Format("{0}//{1}:56737/{2}/psi/{3}.asmx", ss.Protocol, ssHostName, sspName, wsName);
            else
                return string.Format("{0}/_vti_bin/psi/{1}.asmx", ss.Url, wsName);
        }
        #endregion
    }
}
